package dp.mobile.entity;

import java.io.Serializable;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.w3c.dom.Node;

import android.graphics.Point;
import dp.mobile.game.Player;
import dp.mobile.game.Sprite;
import dp.mobile.glwrapper.GLTiledTexture;
import dp.mobile.glwrapper.GameRenderer;
import dp.mobile.level.Level;
import dp.mobile.system.Utilities;

public class Enemy extends Entity implements Serializable {
	private static final long serialVersionUID = -2104385635102987536L;
	//Direction enum
	public static final int DIRECTION_DOWN	= 0;
	public static final int DIRECTION_LEFT	= 1;
	public static final int DIRECTION_UP	= 2;
	public static final int DIRECTION_RIGHT = 3;

	protected Enemy() {
		//Initialize
		mName			= "Musuh gan!";
		mGold			= 0;
		mGoal			= 0;
		mPath			= 0;
		mTileX			= 0;
		mTileY			= 0;
		mOffsetX		= 0;
		mOffsetY		= 0;
		mVelocity		= 0;
		mDirection		= 0;
		mScore			= 0;
		mHealthMax		= 1;
		mHealth			= 1;
		mCenter			= new Point();
		mSpeedModifier	= 1.0f;
		mArmorModifier	= 1.0f;
		mBuffList			= new ArrayList<Buff>();
        mDeletedBuffList	= new ArrayList<Buff>();

		//No array
		mPathX		= null;
		mPathY		= null;
		mSprites	= null;
	}
	
	public Enemy(Node root, GLTiledTexture[][] images, Level level, int pathIndex, Player player) {
		//Initialize
		this();

		//Initialize data
		mPlayer = player;

		//Prepare variable
		int delay			= -1;

		//For each node
		int childCount = root.getChildNodes().getLength();
		for(int i = 0; i < childCount; ++i) {
			//Get tag name and it's value
			Node child 		= root.getChildNodes().item(i);
			if (child.getNodeType() == Node.TEXT_NODE) continue;
			String tag		= child.getNodeName();
			String value	= child.getFirstChild().getNodeValue();

			//Save data
			if(tag.equals("gold"))				mGold 		= Integer.parseInt(value);
			else if (tag.equals("score"))		mScore 		= Integer.parseInt(value);
			else if (tag.equals("path"))		mPath 		= Integer.parseInt(value);
			else if (tag.equals("animation"))	delay 		= Integer.parseInt(value);
			else if (tag.equals("velocity"))	mVelocity 	= Integer.parseInt(value);
			else if (tag.equals("health")) {
                mHealthMax = Integer.parseInt(value);
                mHealth    = mHealthMax;
			}
			
			if (pathIndex != -1) mPath = pathIndex;
			mPathX = level.getPathX(mPath);
			mPathY = level.getPathY(mPath);
			mTileX	= level.getStartX(mPath);
			mTileY	= level.getStartY(mPath);
			

			//Get direction
			calculateDirection();
			
			//Log.d("Enemy", "Enemy created, Path chosen = " + mPath + " path size " + mPathX.length);
		}
		
		//Create sprite
		if (delay >= 0) {
			mSprites = new Sprite[4];
			for (int i = 0; i < mSprites.length; i++)
				mSprites[i] = new Sprite(images[i], 0, 0, delay);
		}
	}

	public int getX()		{	return (mTileX * Utilities.TILE_WIDTH)  + (int)mOffsetX;	}
    public int getY()		{	return (mTileY * Utilities.TILE_HEIGHT) + (int)mOffsetY;	}
	public int getGold()	{	return mGold;												}
	public int getScore()	{	return mScore;												}
	public int getHealth()	{	return mHealth;												}
	public int getMaxHealth() { return mHealthMax; }
	public float getVelocity() { return mVelocity; }
	public int getPath()	{	return mPath;												}
    public Point getCenter(){
    	Sprite current = mSprites[mDirection];
    	mCenter.set(getX() + current.getWidth() / 2, getY() + current.getHeight() / 2);
    	
    	return mCenter;
    }
    
    public void setImages(GLTiledTexture[][] images) {
    	mSprites = new Sprite[4];
		for (int i = 0; i < mSprites.length; i++)
			mSprites[i] = new Sprite(images[i], 0, 0, 0);
    }
    
    public void setGold(int gold) {
    	mGold = gold;
    }
    
    public void setScore(int score) {
    	mScore = score;
    }
    
    public void setVelocity(float val) {
    	mVelocity = val;
    }
    
    public void setMaxHealth(int val) {
    	mHealthMax = val;
    }
	
	protected final void calculateDirection() {
		//Get direction
		if (mPathY[mGoal] < mTileY)			mDirection = Enemy.DIRECTION_UP;
		else if (mPathY[mGoal] > mTileY)	mDirection = Enemy.DIRECTION_DOWN;
		else if (mPathX[mGoal] < mTileX)	mDirection = Enemy.DIRECTION_LEFT;
		else if (mPathX[mGoal] > mTileX)	mDirection = Enemy.DIRECTION_RIGHT;
	}

	public void damage(int damage, Buff buff) {
		//Reduce health
		mHealth -= damage;
		for (int i = 0; i < mSprites.length; i++) mSprites[i].addTint(1, 200);

		//Add buff if not null
		if (buff != null) addBuff(buff);
	}
	
	public void setHealth(int h) {
		mHealth = h;
	}

	protected void addBuff(Buff buff) {
		//Check does it exist?
		int i = 0;
		Buff OldBuff = null;
		while (OldBuff == null && i < mBuffList.size()) {
			//Check
			if (mBuffList.get(i).getIndex() == buff.getIndex()) OldBuff = mBuffList.get(i);
            
			//Next
			i++;
		}

		//Remove old buff
		if (OldBuff != null) removeBuff(OldBuff);

		//Add the buff
		mBuffList.add(buff);
		for (int j = 0; j < mSprites.length; ++j) mSprites[j].addTint(buff.getTint(), buff.getDuration());
	}

	protected void removeBuff(Buff buff) {
		//Remove buff
        mBuffList.remove(buff);

        //mDeletedBuffList.addElement(buff);
	}
	
	public void update(long time) {
		//Check buff
		checkBuff(time);

		//Update sprites
		for (int i = 0; i < mSprites.length; i++) mSprites[i].update(time);

		//Skip if no goal
		if (mGoal < 0) return;

		//Move based on direction
		float offset = mVelocity * mSpeedModifier * time / 1000.0f;
		if (mDirection == Enemy.DIRECTION_DOWN)			mOffsetY += offset;
		else if (mDirection == Enemy.DIRECTION_UP)		mOffsetY -= offset;
		else if (mDirection == Enemy.DIRECTION_LEFT)	mOffsetX -= offset;
		else if (mDirection == Enemy.DIRECTION_RIGHT)	mOffsetX += offset;

		//Update tile
		while (mOffsetX >=  Utilities.TILE_WIDTH)	{	mOffsetX -= Utilities.TILE_WIDTH;  mTileX++;	}
		while (mOffsetX <= -Utilities.TILE_WIDTH)	{	mOffsetX += Utilities.TILE_WIDTH;  mTileX--;	}
		while (mOffsetY >=  Utilities.TILE_HEIGHT)	{	mOffsetY -= Utilities.TILE_HEIGHT; mTileY++;	}
		while (mOffsetY <= -Utilities.TILE_HEIGHT)	{	mOffsetY += Utilities.TILE_HEIGHT; mTileY--;	}

		//Check, has goal reached?
		if (mTileX == mPathX[mGoal] && mTileY == mPathY[mGoal]) {
			//Reset offset
			mOffsetX = 0;
			mOffsetY = 0;

			//Next goal
			mGoal++;
			if (mGoal >= mPathX.length)	mGoal = -1;
			else	calculateDirection();
		}
    }

	protected void checkBuff(long time) {
		//Reset modifier
		mArmorModifier	= 1.0f;
		mSpeedModifier = 1.0f;

		//For each buff
		for (int i = 0; i < mBuffList.size(); i++) {
			//Update
			mBuffList.get(i).update(time, this);
			mArmorModifier *= mBuffList.get(i).getModifier(Buff.ARMOR_MODIFIER);
			mSpeedModifier *= mBuffList.get(i).getModifier(Buff.SPEED_MODIFIER);

			//Check dead buff
			if (!mBuffList.get(i).isActive()) mDeletedBuffList.add(mBuffList.get(i));
		}

		//Delete inactive buffs
		for (int i = 0; i < mDeletedBuffList.size(); i++) removeBuff(mBuffList.get(i));
		mDeletedBuffList.clear();
	}
	
	@Override
	public void draw(GL10 g) {
		
		//Skip if there's no image
		if (mSprites == null) return;

		//Get current sprite
		Sprite Current = mSprites[mDirection];

		//Get rect
		final int left	= (mTileX * Utilities.TILE_WIDTH)  + (int)mOffsetX + ((Utilities.TILE_WIDTH - Current.getWidth()) / 2);
		final int top	= (mTileY * Utilities.TILE_HEIGHT) + (int)mOffsetY - Current.getHeight() / 2 + (int)(Utilities.TILE_HEIGHT * 0.5f);

		
		GameRenderer.instance().draw(g, GameRenderer.instance().getHealthBarImage(mPlayer.getImageIndex()), left + 4, top - 6, (int)((float)mHealth / (float)mHealthMax * (float)(Current.getWidth() - 8)), 4, Utilities.SPRITE_DEPTH);
		
		//Draw sprite
		mSprites[mDirection].setPosition(left, top);
		mSprites[mDirection].draw(g);
	}
	
	//Data
	protected int		mGold;
	protected int		mHealth;
    protected int		mHealthMax;
	protected int		mDirection;
	protected float		mVelocity;
	protected float		mSpeedModifier;
	protected float		mArmorModifier;
	protected int		mPath;
	protected int 		mScore;
	protected Point		mCenter;
	
	protected ArrayList<Buff>	mBuffList;
    protected ArrayList<Buff>	mDeletedBuffList;
    public boolean isActualDead = false;

	//Position
	protected float mOffsetX;
	protected float mOffsetY;

	//Movement
	protected int	mGoal;
	protected int[]	mPathX;
	protected int[]	mPathY;

	//Images
	protected Sprite[] mSprites;
}
